#include "arch/loongarch.h"
#include "lib/stdout.h"
#include "sched/proc.h"
#include "sched/spinlock.h"

void initlock(struct spinlock *lk, char *name) {
	lk->name = name;
	lk->locked = 0;
	lk->cpu = 0;
}

// 获得锁
void acquire(struct spinlock *lk) {
	// 关中断防死锁
	push_off(); 
	if (holding(lk)) {
		pr_error(lock_acquire, "Already holding lock %s", lk->name);
		return;
	}

	// 循环直到获得锁。
	// spin_lock(&lk->locked);
	while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
    ;

	// 告诉C编译器和处理器不要在这一点上移动加载或存储
	// 以确保关键部分的内存引用严格发生在获得锁之后。
	__sync_synchronize();

	// 记录获取锁的 CPU 信息，用于 holding() 和调试。
	lk->cpu = mycpu();
}

// 释放锁
void release(struct spinlock *lk) {
	if (!holding(lk)) {
		pr_error(lock_release, "Not holding lock %s", lk->name);
		return;
	}

	// 标记为没有 CPU 占用
	lk->cpu = 0;

	// 告诉 C 编译器和处理器不要在这一点上移动加载或存储
	// 以确保关键部分的内存引用严格发生在获得锁之后。
	__sync_synchronize();

	// 释放锁，相当于 lk->locked = 0。
	// 代码不能使用C语言的赋值，因为C标准意味着赋值可能会用多条存储指令实现。
	// spin_unlock(&lk->locked);
	__sync_lock_release(&lk->locked);

	pop_off();
}

// 检查这个 CPU 是否持有锁。
// 调用时必须确保中断是关闭的。
int holding(struct spinlock *lk) {
	int r;
	r = (lk->locked && lk->cpu == mycpu());
	return r;
}

// push_off 就像 intr_off()。
// 它们是两两匹配的：需要两个pop_off()来撤销两个push_off()。 
// 另外，如果中断最初是关闭的，那么push_off，pop_off会使它们关闭。
void push_off(void) {
	int old = intr_get();

	intr_off();
	if (mycpu()->noff == 0)
		mycpu()->intena = old;
	mycpu()->noff += 1;
}

// pop_off 就像 intr_on()。
// 它们是两两匹配的：需要两个pop_off()来撤销两个push_off()。 
// 另外，如果中断最初是关闭的，那么push_off，pop_off会使它们关闭。
void pop_off(void) {
	struct cpu *c = mycpu();
	if (intr_get())
		panic(pop_off, "interrupts enabled");
	if (c->noff < 1)
		panic(pop_off, "c->noff < 1");
	c->noff -= 1;
	if (c->noff == 0 && c->intena)
		intr_on();
}
